<?php

namespace july\julyadmin\builder\traits\actions;

use july\julyadmin\builder\Builder;
use july\julyadmin\builder\table\DataTable;

/**
 * 列表基类
 * @author July
 */
trait HasIndex
{
    //第几页
    protected $page = 1;
    //每页多少条
    protected $limit = 20;
    //总共多少条
    protected $total = 0;
    //排序
    protected $sort = 'id desc';
    //join关联查询的表信息如，['category'=>'inner','comment'=>'left']
    protected $relationWithJoins = ['category' => 'inner'];
    //with预查询关联表信息，如category,comment
    protected $relationWiths = [];
    //表格类型,table,treeTable
    protected $tableType = 'table';
    //父id字段名称，如parent_id,当表格为treeTable的时候，意味着你要输出的数据也会是树形的，这个时候需要指定父级字段名称，如果有配置了$this->selectRelationWithJoins，这里就需要注意了，排序字段需要带上表名加一个点，如admin.，根据实际需求修改
    protected $parentField = 'parent_id';
    //查询第一层数据列表条件，如[['parent_id','=',0]],当表格为treeTable的时候，意味着你要输出的数据也会是树形的，这个时候首次查询最父层的数据列表可能需要传入这个参数，根据实际需求修改
    protected $oneParentWhere = [['parent_id', '=', 0]];
    //树形表格分组字段，比如name，那么展开和搜索箭头就会在name这一列显示，当表格为treeTable的时候会用到
    protected $treeTableGroupField = 'name';
    //需要合计的字段，如['显示在哪个字段','显示哪个字段的值']，如果有配置了$this->selectRelationWithJoins，这里就需要注意了，排序字段需要带上表名加一个点，如['id'=>'admin.sex','money'=>admin.money']
    protected $totalRow = [];
    //表格
    protected $table;

    public function index($page = 1, $limit = 20, $sort = '', $search = array())
    {
        $this->table = new DataTable($this);
        //设置分组字段
        $this->table->treeTableGroupField($this->treeTableGroupField);
        //设置表格id
        $this->table->tableType($this->tableType)->tableId(request()->controller() . request()->action() . "_table");
        //设置表格请求地址
        $this->table->datagridUrl($_SERVER['REQUEST_URI']);
        //渲染页面
        $this->buildPage($this->table);
        if (request()->isPost()) {
            $where      = $this->filterWhere($search);
            $this->page = $page;
            if ($limit) {
                $this->limit = $limit;
            }

            if ($sort) {
                $this->sort = $sort;
            }

            // //如果有连表操作，并且dataModel有实例化对象，那么判断一下排序条件有没有带表名，没有就处理一下加上
            if ($this->relationWithJoins && $this->dataModel) {
                $sorts = array_filter(explode(',', $this->sort));
                array_walk($sorts, function (&$sort_value, $key) {
                    if (stripos($sort_value, '.') === false) {
                        $class      = class_basename($this->dataModel);
                        $class      = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $class));
                        $sort_value = $class . '.' . $sort_value;
                    }
                });
                $this->sort = implode(',', $sorts);
            }

            $data = $this->buildDataList($where, $this->limit, $this->sort, $this->page, $this->total);
            if (isset($data['data'])) {
                $data['data'] = $this->handleToValue($this->table->form->parent->table->form->to, $data['data']);
            }
            return json($data);
        } else {
            $render  = $this->table->render();
            $builder = new Builder();
            //渲染顶部
            $builder->renderTop(false);

            echo <<<EOT

                <div class="container-fluid">
                    <div class="card">
                        <header class="card-header">
                            <div class="card-title">{$this->pageTitle}</div>
                        </header>
                        <div class="card-body" style="padding:0px;">
                            {$render['html']}
                            {$render['script']}
                        </div>
                    </div>
                </div>
EOT;

            //渲染底部
            $builder->renderBottom();

        }
    }

    /**
     * 处理to自定义数据格式，可以实现丰富的样式
     * @Author
     * @DateTime 2023-08-07T22:31:42+0800
     * @param    array                   $to   to数组
     * @param    array                   $data 表格数据数组
     * @return   array                   $data 处理过后的表格数据列表
     */
    protected function handleToValue($to = [], $data = [])
    {
        //处理to数据
        if ($to) {
            $tos = $to;
            foreach ($data as $data_k => $data_v) {
                foreach ($tos as $tok => $tov) {
                    $value = $tov;
                    $preg  = "/\{([a-zA-Z_0-9\.]+)\}/";
                    preg_match_all($preg, $tov, $res);
                    if (isset($res[1]) && $res[1]) {
                        foreach ($res[1] as $field) {
                            if (stripos($field, '.') > -1) {
                                $fields   = array_filter(explode('.', $field));
                                $valueObj = $data_v[$fields[0]];
                                if ($valueObj) {
                                    foreach ($fields as $field_key => $field_value) {
                                        if (isset($valueObj[$field_value])) {
                                            $valueObj = $valueObj[$field_value];
                                        }
                                    }
                                } else {
                                    $valueObj = '空';
                                }
                                if ($valueObj === '') {
                                    $valueObj = '空';
                                }
                                $tovalue = $valueObj;
                                if (is_array($tovalue) || is_object($tovalue)) {
                                    $tovalue = '空';
                                }
                                $value = str_replace('{' . $field . '}', $tovalue, $value);
                            } else {
                                if (is_array($data_v[$field]) || is_object($data_v[$field])) {
                                    $data_v[$field] = '空';
                                }
                                $tovalue = isset($data_v[$field]) ? $data_v[$field] : '空';
                                $value   = str_replace('{' . $field . '}', $tovalue, $value);
                            }
                        }
                    }
                    $data[$data_k][$tok] = $value;
                }
            }
        }
        return $data;
    }

    /**
     * 渲染页面
     * @param  string $pageTitle 页面标题
     * @param  string $body      渲染内容
     */
    protected function renderPage($pageTitle = '', $body = '')
    {
        if (!$pageTitle) {
            $pageTitle = $this->pageTitle;
        }
        $builder = new Builder();
        //渲染顶部
        $builder->renderTop(false);

        echo <<<EOT
            <div class="container-fluid">
                <div class="card">
                    <header class="card-header">
                        <div class="card-title">{$pageTitle}</div>
                    </header>
                    <div class="card-body" style="padding:0px;">
                        {$body}
                    </div>
                </div>
            </div>
EOT;

        //渲染底部
        $builder->renderBottom();
    }

    /**
     * 处理查询条件
     * 此方法可在控制器中重写
     * @param array $search 查询条件
     * @return array         组装好的查询条件
     */
    protected function filterWhere($search = [])
    {
        return [];
    }

    /**
     * 生成数据
     * 此方法可在控制器中重写
     * @param array   &$where 查询条件数组
     * @param integer $page 第几页
     * @param integer $limit 每页多少条
     * @param string $sort 排序字段和排序
     * @param integer &$total 总共多少条
     */
    protected function buildDataList(&$where = [], $limit = 20, $sort = '', $page = 1, &$total = 0)
    {
        $db = $this->dataModel;
        foreach ($this->relationWithJoins as $relation_k => $relation_v) {
            $db = $db->withJoin($relation_k, $relation_v);
        }

        if ($this->tableType == 'treeTable') {
            $list = $db
                ->with($this->relationWiths)
                ->where(array_merge($where, $this->oneParentWhere))
                ->order($sort)
                ->limit(($page - 1) * $limit, $limit)
                ->select()
                ->toArray();

            $total = $db
                ->with($this->relationWiths)
                ->where(array_merge($where, $this->oneParentWhere))
                ->count();
        } else {
            $list = $db
                ->with($this->relationWiths)
                ->where($where)
                ->order($sort)
                ->limit(($page - 1) * $limit, $limit)
                ->select()
                ->toArray();

            $total = $db
                ->with($this->relationWiths)
                ->where($where)
                ->count();
        }

        foreach ($list as $list_k => $list_v) {
            if ($this->noEditPrimarykeys && in_array($list_v[$this->pk], $this->noEditPrimarykeys)) {
                $list[$list_k]['__edit_hidden__'] = true;
            }
            if ($this->noDisabledPrimarykeys && in_array($list_v[$this->pk], $this->noDisabledPrimarykeys)) {
                $list[$list_k]['__disabled_hidden__'] = true;
            }
            if ($this->noDeletePrimarykeys && in_array($list_v[$this->pk], $this->noDeletePrimarykeys)) {
                $list[$list_k]['__delete_hidden__'] = true;
            }
        }

        //如果是树形表格，处理一下，递归获取下级信息
        if ($this->tableType == 'treeTable') {
            foreach ($list as $k => $v) {
                $children             = $this->getChildren($where, $sort, $v[$this->pk]);
                $list[$k]['iconLeaf'] = false;
                if ($children) {
                    $list[$k]['isParent'] = true;
                } else {
                    $list[$k]['isParent'] = false;
                }
                $list[$k]['children'] = $children;
            }
        }

        $this->handleData($list);

        return ['code' => 0, 'data' => $list, 'count' => $total, 'totalRow' => $this->getTotalRow()];
    }

    private function getTotalRow($where = [], $limit = 20, $sort = '', $page = 1)
    {
        $db = $this->dataModel;
        foreach ($this->relationWithJoins as $relation_k => $relation_v) {
            $db = $db->withJoin($relation_k, $relation_v);
        }
        $data = [];
        foreach ($this->totalRow as $total_row_k => $total_row_v) {
            if ($this->tableType == 'treeTable') {
                $data[$total_row_k] = $db
                    ->with($this->relationWiths)
                    ->where(array_merge($where, $this->oneParentWhere))
                    ->order($sort)
                    ->limit(($page - 1) * $limit, $limit)
                    ->sum($total_row_v);
            } else {
                $data[$total_row_k] = $db
                    ->with($this->relationWiths)
                    ->where($where)
                    ->order($sort)
                    ->limit(($page - 1) * $limit, $limit)
                    ->sum($total_row_v);
            }
        }

        $result = $this->filterTotalRow($where, $limit, $sort, $page, $data);
        return $result;
    }

    /**
     * 递归获取下级列表
     * @param  integer $parent_id 父id
     * @param  array   $where     查询条件
     * @param  string  $sort      排序
     */
    private function getChildren($where, $sort, $parent_id = 0)
    {
        $db = $this->dataModel;
        foreach ($this->relationWithJoins as $relation_k => $relation_v) {
            $db = $db->withJoin($relation_k, $relation_v);
        }
        $data = $db->where(array_merge($where, [[$this->parentField, '=', $parent_id]]))->order($sort)->select()->toArray();
        if ($data && (is_array($data) || is_object($data))) {
            $this->handleData($data, count($data));
            $data = $this->handleToValue($this->table->form->parent->table->form->to, $data);
            foreach ($data as &$arr) {
                $arr['iconLeaf'] = false;
                $arr['children'] = $this->getChildren($where, $sort, $arr[$this->pk]);
                if ($arr['children']) {
                    $arr['isParent'] = true;
                } else {
                    $arr['isParent'] = false;
                }
            }
        }

        return $data;
    }

    /**
     * 处理数据
     * 当前方法可以重写
     * 因为查询出来的数据可能需要二次处理
     * @param  array &$data 查询出来的数据
     */
    protected function handleData(&$data = [])
    {

    }

    /**
     * 构建页面
     * 当前方法可以重写
     * @param $table  表格
     */
    protected function buildPage(&$table)
    {

    }

    /**
     * 处理合计行信息
     * 当前方法可以重写
     * @Author   July
     * @DateTime 2023-11-16T20:45:54+0800
     * @param    array                   $where    已经处理过的最终查询条件，可以自己覆盖，用来自己重新查询数据
     * @param    array                   $totalRow 已经查询出来的合计信息，可以自己覆盖
     * @return   array                   $totalRow 将处理过的最终信息返回
     */
    protected function filterTotalRow($where = [], $totalRow = [])
    {
        return $totalRow;
    }

    /**
     * 构建表单
     * 当前方法可以重写
     * @param $isEdit  是否是编辑
     * @param $data    查询数据
     */
    protected function buildForm(&$form, $isEdit, &$data = [])
    {

    }
}
